﻿
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace S7Communication
{
    public class RecieveBufferAnalysis
    {
        private AnalysisData analysisData = new AnalysisData();
        public AnalysisData AnalysisData { get { return analysisData; } }
        private object Locker = new object();
        /// <summary>
        /// 接受数据解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void BufferAnalysisAsync(byte[] data)
        {
            lock(Locker)
            { 
                //会话层4个字节
                if (data == null) { throw new Exception("会话层/接收数据为空"); }
                if (data.Length < 8) { throw new Exception("会话层/接受数据太短"); }
                analysisData.tpkt_version = data[0];
                try { analysisData.tpkt_length = BitConverter.ToUInt16(data.Skip(2).Take(2).Reverse().ToArray()); }
                catch (Exception ex) { throw new Exception("会话层/接受数据总长度获取失败", ex); }
                if (data.Length != analysisData.tpkt_length) { throw new Exception("会话层/接受数据总长度错误"); }
                COTP_Analysis(data.Skip(4).ToArray());
            }
        }
        /// <summary>
        /// 表示层PDU解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        void COTP_Analysis(byte[] data)
        {
            analysisData.cotp_length = data[0];
            try { analysisData.cotp_pduType = (COTP_PDU_TYPE)data[1]; } catch { throw new Exception("会话层/表示层/pduType类型错误"); }
            analysisData.cotp_paramsCount = 0;
            analysisData.cotp_destinationRef = BitConverter.ToUInt16(data.Skip(2).Take(2).Reverse().ToArray());
            if (data.Length - 1 != analysisData.cotp_length) { throw new Exception("会话层/表示层/数据长度错误"); }
            switch (analysisData.cotp_pduType)//pdu类型
            {
                case COTP_PDU_TYPE.ED:
                    throw new Exception("会话层/表示层/pdu类型：加急数据/暂不支持该类型");
                case COTP_PDU_TYPE.EA:
                    throw new Exception("会话层/表示层/pdu类型：加急数据确认/暂不支持该类型");
                case COTP_PDU_TYPE.UD:
                    throw new Exception("会话层/表示层/pdu类型：用户数据/暂不支持该类型");
                case COTP_PDU_TYPE.RJ:
                    throw new Exception("会话层/表示层/pdu类型：拒绝/暂不支持该类型");
                case COTP_PDU_TYPE.AK:
                    throw new Exception("会话层/表示层/pdu类型：数据确认/暂不支持该类型");
                case COTP_PDU_TYPE.ER:
                    throw new Exception("会话层/表示层/pdu类型/：TPDU错误/暂不支持该类型");
                case COTP_PDU_TYPE.DR:
                    throw new Exception("会话层/表示层/pdu类型：断开请求/暂不支持该类型");
                case COTP_PDU_TYPE.DC:
                    throw new Exception("会话层/表示层/pdu类型：断开确认/暂不支持该类型");
                case COTP_PDU_TYPE.CC:
                    if (data.Length < 7) { throw new Exception("会话层/表示层/pdu类型：连接确认/数据长度错误"); }
                    analysisData.cotp_sourcenRef = BitConverter.ToUInt16(data.Skip(4).Take(2).Reverse().ToArray());//源索引
                    analysisData.cotp_opt = data[6];
                    analysisData.cotp_parameters.Clear();
                    analysisData.cotp_paramsCount = 0;
                    if (data.Length == 7) { return; }
                    COTP_ParamenterAnalysis(data.Skip(7).ToArray());//解析参数
                    break;
                case COTP_PDU_TYPE.CR:
                    throw new Exception("会话层/表示层/pdu类型：连接请求/暂不支持该类型");
                case COTP_PDU_TYPE.DT:
                    if (data.Length < 6) { throw new Exception("会话层/表示层/pdu类型：数据传输/数据长度太短"); }
                    DT_HeaderAnalysis(data.Skip(4).ToArray());
                    break;
                default:
                    throw new Exception("会话层/表示层/pdu类型：未知");
            }
        }
        /// <summary>
        /// 表示层连接参数解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns>解析结果</returns>
        void COTP_ParamenterAnalysis(byte[] data)
        {
            int index = 0;
            while (true)
            {
                if (data.Length == index) { return; };
                if (data.Length < index + 2) { throw new Exception("会话层/表示层/pdu类型：连接确认/参数长度太短"); };
                COTP_Parameter cotp_parameter = new COTP_Parameter();
                cotp_parameter.paramCode = data[index];
                cotp_parameter.paramLength = data[index + 1];
                if (data.Length < index + 2 + cotp_parameter.paramLength) { throw new Exception("会话层/表示层/pdu类型：连接确认/参数长度错误"); }
                cotp_parameter.param = new byte[cotp_parameter.paramLength];
                Array.Copy(data, 2, cotp_parameter.param, 0, cotp_parameter.paramLength);
                analysisData.cotp_parameters.Add(cotp_parameter);
                analysisData.cotp_paramsCount++;
                if (cotp_parameter.paramCode == 0xc1) analysisData.cotp_rack = BitConverter.ToUInt16(cotp_parameter.param.Reverse().ToArray());
                if (cotp_parameter.paramCode == 0xc2) analysisData.cotp_solt = BitConverter.ToUInt16(cotp_parameter.param.Reverse().ToArray());
                index = index + 2 + cotp_parameter.paramLength;
            }
        }
        /// <summary>
        /// 数据传输头部解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        void DT_HeaderAnalysis(byte[] data)
        {
            if (data.Length < 10) { throw new Exception("会话层/表示层/pdu类型：数据传输/头部长度太短"); }
            analysisData.dt_header_protocolID = data[0];
            try { analysisData.dt_header_roscrt = (DT_ROSCTR)data[1]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT类型错误"); }
            analysisData.dt_header_reserve = BitConverter.ToUInt16(data, 2);
            analysisData.dt_header_sequenceID = BitConverter.ToUInt16(data, 4);
            analysisData.dt_header_paramLength = BitConverter.ToUInt16(data.Skip(6).Take(2).Reverse().ToArray());
            analysisData.dt_header_dataLength = BitConverter.ToUInt16(data.Skip(8).Take(2).Reverse().ToArray());
            switch (analysisData.dt_header_roscrt)
            {
                case DT_ROSCTR.JOB:
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：作业请求/暂不支持该请求");
                case DT_ROSCTR.ACK:
                    return;
                case DT_ROSCTR.ACK_DATA:
                    if (data.Length < 12) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/头部长度太短"); }
                    analysisData.dt_header_errorClass = data[10];
                    analysisData.dt_header_errorCode = data[11];
                    if (analysisData.dt_header_paramLength < 2) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/参数长度太短"); }
                    if (data.Length - 12 != analysisData.dt_header_paramLength + analysisData.dt_header_dataLength) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/参数长度加数据长度错误"); }
                    DT_ParamenterAnalysis(data.Skip(12).ToArray());
                    break;
                case DT_ROSCTR.USERDATA://原始协议扩展，参数字段包含请求/响应ID
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：原始协议扩展/暂不支持该请求");
                default:
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：未知");
            }
        }
        /// <summary>
        /// 应用层数据传输解析
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        void DT_ParamenterAnalysis(byte[] data)
        {
            try { analysisData.dt_parameter_function = (DT_PARAMETER_FUCTION)data[0]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码错误"); }
            analysisData.dt_parameter_itemCount = data[1];
            switch (analysisData.dt_parameter_function)
            {
                case DT_PARAMETER_FUCTION.Setup_Communication:
                    if (data.Length != 8) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：建立通信/数据长度错误"); }
                    analysisData.dt_stepCommunication_maxAmQCalling = BitConverter.ToUInt16(data.Skip(2).Take(2).Reverse().ToArray());
                    analysisData.dt_stepCommunication_maxAmQCalled = BitConverter.ToUInt16(data.Skip(4).Take(2).Reverse().ToArray());
                    analysisData.dt_stepCommunication_pduLength = BitConverter.ToUInt16(data.Skip(6).Take(2).Reverse().ToArray());
                    return;
                case DT_PARAMETER_FUCTION.Read_Variable://读取值
                    if (data.Length < 2) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/参数长度错误"); }
                    analysisData.dt_items.Clear();
                    if (analysisData.dt_parameter_itemCount == 0 && data.Length > 2) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/数据长度错误"); }
                    if (analysisData.dt_parameter_itemCount == 0) { return; }
                    DT_ReadDataAnalysis(data.Skip(2).ToArray());
                    return;
                case DT_PARAMETER_FUCTION.Write_Variable://写入值
                    analysisData.dt_items.Clear();
                    if (data.Length - 2 != analysisData.dt_parameter_itemCount) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：写入值/数据长度错误"); };
                    if (analysisData.dt_parameter_itemCount == 0) { return; };
                    //写入结果
                    AckItem item = new AckItem();
                    for (int i = 1; i <= analysisData.dt_parameter_itemCount;)
                    {
                        item.returnCode = (DT_RETRUN_CODE)data[i + 1];
                        analysisData.dt_items.Add(item);
                    }
                    return;
                default:
                    throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：未知");
            }
        }
        void DT_ReadDataAnalysis(byte[] data)
        {
            int index = 0;
            AckItem item = new AckItem();
            for (int i = 1; i <= analysisData.dt_parameter_itemCount;)
            {
                if (data.Length < index + 4) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item长度错误"); }
                //获取数据和格式和结果                     
                try { item.returnCode = (DT_RETRUN_CODE)data[index]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item的returnCode类型错误"); }
                try { item.transportSizes = (DT_DATA_TRANSPORT_SIZI)data[index + 1]; } catch { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item的transportSizes类型错误"); }
                item.dataLength = BitConverter.ToUInt16(data.Skip(6).Take(index + 2).Reverse().ToArray());//数据长度

                if (data.Length < index + 2 + item.dataLength) { throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/item的数据长度错误"); }
                item.data=new byte[item.dataLength];
                Array.Copy(data, index + 2, item.data, 0, item.dataLength);
                analysisData.dt_items.Add(item);
                index = index + 4 + item.dataLength;
            }
            if (data.Length == index) { return; };
            throw new Exception("会话层/表示层/pdu类型：数据传输/ROSCRT：确认数据响应/功能码：读取值/数据总长度错误");
        }
    }
}
